package com.duoec.graphql.pipeline.impl;

import com.duoec.graphql.core.GraphqlConsts;
import com.duoec.graphql.core.GraphqlModuleContext;
import com.duoec.graphql.core.exception.GraphqlBuildException;
import com.duoec.graphql.pipeline.Pipeline;
import com.duoec.graphql.pipeline.RegistryState;
import com.duoec.graphql.provider.dto.DuoDocGraphqlProviderServiceInfo;
import com.duoec.graphql.provider.dto.ProviderModelInfo;
import com.duoec.graphql.provider.dto.provider.ProviderApiDto;
import com.duoec.graphql.service.DuoDocService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * API文档加载
 *
 * @author xuwenzhen
 * @date 2019/6/3
 */
@Service
public class ApiDataLoadPipeline implements Pipeline {
    @Autowired
    private DuoDocService duoDocService;

    /**
     * 处理
     *
     * @param state 当前注册信息
     */
    @Override
    public void doPipeline(RegistryState state) {
        List<DuoDocGraphqlProviderServiceInfo> providers = state.getProviderServices();
        List<CompletableFuture<ProviderApiDto>> futures = Lists.newArrayList();
        providers.forEach(provider -> {
            //拉接口文档的服务
            CompletableFuture<ProviderApiDto> providerApiFuture = CompletableFuture.supplyAsync(
                    //从Duo-Doc服务中拉取文档数据
                    () -> duoDocService.fetchDocData(provider.getAppId(), provider.getVcsId(), null)
            );
            futures.add(providerApiFuture);
        });

        if (CollectionUtils.isEmpty(futures)) {
            return;
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        for (int i = 0; i < futures.size(); i++) {
            CompletableFuture<ProviderApiDto> future = futures.get(i);
            ProviderApiDto providerApi;
            try {
                providerApi = future.get();
            } catch (Exception e) {
                throw new GraphqlBuildException("get provider api info from Duo-Doc error", e);
            }
            if (providerApi == null) {
                continue;
            }
            DuoDocGraphqlProviderServiceInfo provider = providers.get(i);

            List<GraphqlModuleContext> contextList = getGraphqlModuleContextList(providerApi, provider);
            if (!CollectionUtils.isEmpty(contextList)) {
                contextList.forEach(state::putRemoteProvider);
            }
        }
    }

    private List<GraphqlModuleContext> getGraphqlModuleContextList(ProviderApiDto providerApi, DuoDocGraphqlProviderServiceInfo provider) {
        //注册默认的模块
        if (CollectionUtils.isEmpty(provider.getModuleMap())) {
            GraphqlModuleContext moduleContext = new GraphqlModuleContext(providerApi, provider);
            return Lists.newArrayList(moduleContext);
        }

        String defaultModuleName = provider.getModuleName();
        Map<String, String> controllerModuleMap = Maps.newHashMap();
        provider.getModuleMap().forEach((moduleName, controller) -> controllerModuleMap.put(controller, moduleName));
        Map<String, ProviderApiDto> moduleProviderApiDotMap = Maps.newHashMap();
        List<GraphqlModuleContext> contextList = Lists.newArrayList();
        List<ProviderModelInfo> modules = provider.getModels();
        providerApi.getApis().forEach(api -> {
            String code = api.getCode();
            int index = code.lastIndexOf(GraphqlConsts.STR_DOT);
            String controller = code.substring(0, index);
            String moduleName = controllerModuleMap.getOrDefault(controller, defaultModuleName);
            moduleProviderApiDotMap.computeIfAbsent(moduleName, mn -> {
                ProviderApiDto providerApiDto = new ProviderApiDto();
                providerApiDto.setApis(Lists.newArrayList());
                providerApiDto.setEntities(providerApi.getEntities());
                DuoDocGraphqlProviderServiceInfo newProvider = new DuoDocGraphqlProviderServiceInfo();
                BeanUtils.copyProperties(provider, newProvider);

                List<ProviderModelInfo> newModels = modules.stream().filter(module -> module.getModelName().equalsIgnoreCase(moduleName)).collect(Collectors.toList());
                newProvider.setModels(newModels);

                newProvider.setModuleName(moduleName);
                contextList.add(new GraphqlModuleContext(providerApiDto, newProvider));
                return providerApiDto;
            }).getApis().add(api);
        });
        return contextList;
    }

    /**
     * 执行的顺序
     *
     * @return 数值，越小越前
     */
    @Override
    public int order() {
        return 100;
    }
}
